package org.hepeng.workx.spring.boot.error;

import lombok.Builder;
import lombok.Data;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hepeng.workx.spring.cloud.netflix.zuul.route.AbstractZuulRoutingRewriteMatch;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author he peng
 */

@Builder
@Data
public class ErrorBody {

    protected static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper();

    private String targetService;
    private String timestamp;
    List<Map<String , String>> errors;
    private String path;

    public static ErrorBody newErrorBody(
            NativeWebRequest request , ErrorAttributes errorAttributes , Environment env , RouteLocator routeLocator) {
        Assert.notNull(request, "request must not be null");
        Assert.notNull(errorAttributes, "errorAttributes must not be null");
        Assert.notNull(env, "env must not be null");
        Assert.notNull(routeLocator, "routeLocator must not be null");

        Map<String, Object> errorAttrs = errorAttributes.getErrorAttributes(request, true);
        Throwable error = errorAttributes.getError(request);

        List<Map<String , String>> errors = new ArrayList<>();
        Map<String , String> errorMap = new HashMap<>();
        errorMap.put("error" , error.getClass().getName());
        errorMap.put("errorMsg" , ExceptionUtils.getMessage(error));

        Throwable rootError = ExceptionUtils.getRootCause(error);
        String rootException = Objects.nonNull(rootError) ? rootError.getClass().getName() : "";
        String rootExceptionMsg = Objects.nonNull(rootError) ? ExceptionUtils.getMessage(rootError) : "";
        errorMap.put("rootError" , rootException);
        errorMap.put("rootErrorMsg" , rootExceptionMsg);
        errors.add(errorMap);

        ErrorBody errorBody = ErrorBody.builder()
                .targetService(getTargetService(request.getNativeRequest(HttpServletRequest.class) , env , routeLocator))
                .timestamp(DateFormatUtils.format(new Date(), "yyyy-mm-dd HH:mm:ss"))
                .path((String) errorAttrs.get("path"))
                .errors(errors)
                .build();

        return errorBody;
    }

    private static String getTargetService(HttpServletRequest request , Environment env , RouteLocator routeLocator) {
        Object attr = request.getAttribute(AbstractZuulRoutingRewriteMatch.TARGET_SERVICE_REQUEST_ATTR);
        if (Objects.nonNull(attr)) {
            return (String) attr;
        }
        Route route = routeLocator.getMatchingRoute(URL_PATH_HELPER.getRequestUri(request));
        return Objects.nonNull(route) ? route.getId() : env.getProperty("spring.application.name");
    }

}
